package com.jt.aop;

import com.jt.annotation.CacheFind;
import com.jt.pojo.ItemDesc;
import com.jt.util.ObjectMapperUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.ShardedJedis;
import sun.misc.Cache;

import java.util.Arrays;

@Aspect     //标识我是一个切面
@Component  //将对象交给spring容器管理  cacheAOP
public class CacheAOP {

    @Autowired
    //private Jedis jedis;  //redis单台测试
    //private ShardedJedis jedis; //redis分片操作
    //private JedisSentinelPool sentinelPool;  //注入池对象
    private JedisCluster jedis;          //注入集群对象

    /**
     * 实现思路:
     *      1.动态获取key   用户自定义的前缀+用户的参数[0,xx]
     *      2.判断key是否存在
     *             存在: 直接从redis中获取数据 JSON, 需要将json转化为具体对象  按照返回值类型进行转化
     *             不存在: 执行目标方法.获得返回值数据. 将返回值结果转化为JSON格式. 之后保存到缓存中.
     * @param joinPoint
     * @return
     */
    @Around("@annotation(cacheFind)")
    public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind) throws NoSuchMethodException {
        //Jedis jedis = sentinelPool.getResource();
        Object result = null;
        //1.获取key的前缀
        String key = cacheFind.key();
        //2.获取方法参数
        String argString = Arrays.toString(joinPoint.getArgs());
        key = key + "::" + argString;
        try {
             //3.判断缓存中是否有数据
            if(jedis.exists(key)){
                String json = jedis.get(key);
                //5.获取返回值类型
                MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
                result = ObjectMapperUtil.toObject(json,methodSignature.getReturnType());
                System.out.println("AOP查询redis缓存");
            }else{
                //表示缓存中没有数据,执行目标方法
                result = joinPoint.proceed();
                String json = ObjectMapperUtil.toJSON(result);
                //4.判断数据中是否有超时时间
                if(cacheFind.seconds()>0){
                    jedis.setex(key,cacheFind.seconds(),json);
                }else{
                    jedis.set(key,json);
                }
                System.out.println("AOP执行数据库调用!!!!!");
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            throw new RuntimeException(throwable);
        }

        //jedis.close();  //还池操作
        return result;
    }


















    //切面 = 切入点表达式 + 通知方法
    //表达式1: bean(itemCatServiceImpl)  ItemCatServiceImpl类
    //@Pointcut("bean(itemCatServiceImpl)")
    //@Pointcut("within(com.jt.service.*)")
        // .* 一级包下的类   ..* 所有子孙后代的包和类
        //返回值类型任意, com.jt.service包下的所有类的add方法参数类型任意类型
        //写参数类型时注意类型的大小写
   /* @Pointcut("execution(* com.jt.service..*.*(..))")
    public void pointcut(){

    }*/
    /**
     *
     * joinPoint代表连接点对象,一般适用于前四大通知类型(除around之外的)
     *        客人                         路人
     */
    /*@Before("pointcut()")
    public void before(JoinPoint joinPoint){
        //1.获取目标对象
        Object target = joinPoint.getTarget();
        System.out.println(target);
        //2.获取目标对象的路径 包名.类名.方法名
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        System.out.println("目标方法的路径:"+(className+"."+methodName));
        //3.获取参数类型
        System.out.println(Arrays.toString(joinPoint.getArgs()));
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint){
        System.out.println("环绕通知执行");
        Object data = null;
        try {
            data = joinPoint.proceed(); //执行目标方法
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return data;
    }*/
}
